/**
*3D的矩阵
*  矩阵是干啥的，就是把一个坐标系里的点变换到另一个坐标系里去，两个坐标系的关系可以构造出矩阵
*  
*  矩阵是负责把在A坐标系里的点P的坐标,转换到在B坐标系里的点P‘的坐标
*  知道A坐标系和B坐标系的关系，可以构造出此转换矩阵
*  构造方法是，B的每个基向量在A坐标系里的坐标作为矩阵的行向量。
*  注意，此时假设A坐标系和B坐标系是原点对齐的，想平移，则使用tx,ty,tz即可
*  41,42,43是tx,ty,tz
*/
package kgame5.k3d.core.math {
import kgame5.kmath.MathDG;
import kgame5.kutil.*;

public class Matrix3D{ 
	//__________________Property________________
	public var 
		m11:Number,m12:Number,m13:Number,m14:Number,
		m21:Number,m22:Number,m23:Number,m24:Number,
		m31:Number,m32:Number,m33:Number,m34:Number,
		m41:Number,m42:Number,m43:Number,m44:Number;
	//__________________Method______________
	public function Matrix3D(){
		Identity();
	}
	//打印
	public function toString():String{
		return (m11+" "+m12+" "+m13+" "+m14+"\n"+
		m21+" "+m22+" "+m23+" "+m24+"\n"+
		m31+" "+m32+" "+m33+" "+m34+"\n"+
		m41+" "+m42+" "+m43+" "+m44);
	}
	public function Identity():void{
		m11=1;m12=0;m13=0;m14=0;
		m21=0;m22=1;m23=0;m24=0;
		m31=0;m32=0;m33=1;m34=0;
		m41=0;m42=0;m43=0;m44=1;
	}
	//判断两个是否相同
	public function isEqual(m:Matrix3D):Boolean{
		var b:Boolean= 
			m11==m.m11&&m12==m.m12&&m13==m.m13&&m14==m.m14&&
			m21==m.m21&&m22==m.m22&&m23==m.m23&&m24==m.m24&&
			m31==m.m31&&m32==m.m32&&m33==m.m33&&m34==m.m34&&
			m41==m.m41&&m42==m.m42&&m43==m.m43&&m44==m.m44;
			
		return b;
	}
	//进行复制,把一个值copy到另一个
	public function copyTo(m:Matrix3D):void{
		m.m11=m11;m.m12=m12;m.m13=m13;m.m14=m14;
		m.m21=m21;m.m22=m22;m.m23=m23;m.m24=m24;
		m.m31=m31;m.m32=m32;m.m33=m33;m.m34=m34;
		m.m41=m41;m.m42=m42;m.m43=m43;m.m44=m44;
	}
	//直接设置矩阵的平移部分
	public function setTranslation(x:Number,y:Number,z:Number):void{
		m41=x;m42=y;m43=z;
	}
	//不实现
	//public function setupTranslation(){}
	//饶某轴转多少度
	public function setupRotate(x:Number,y:Number,z:Number,angle:Number):void{
		var rad:Number=angle*MathDG.DTR;//弧度
		var s:Number=Math.sin(rad);
		var c:Number=Math.cos(rad);
		
		/*
		m11=x*x*(1-c)+c;m12=x*y*(1-c)+z*s;m13=x*z*(1-c)-y*s;m14=0;
		m21=x*z*(1-c)-z*s;m22=y*y*(1-c)+c;m23=y*z*(1-c)+x*s;m24=0;
		m31=x*z*(1-c)+y*s;m32=y*z*(1-c)-x*s;m33=z*z*(1-c)+c;m34=0;
		m41=0;m42=0;m43=0;m44=1;
		//*/
		
		///////快速4*3
		/*
		m11=x*x*(1-c)+c;m12=x*y*(1-c)+z*s;m13=x*z*(1-c)-y*s;//m14=0;
		m21=x*z*(1-c)-z*s;m22=y*y*(1-c)+c;m23=y*z*(1-c)+x*s;//m24=0;
		m31=x*z*(1-c)+y*s;m32=y*z*(1-c)-x*s;m33=z*z*(1-c)+c;//m34=0;
		//m41=0;m42=0;m43=0;m44=1;
		 * 
		 */
		 
		///////更快速4*3！！
		var xx:Number=x*x;
		var yy:Number=y*y;
		var zz:Number=z*z;
		
		var xy:Number=x*y;
		var xz:Number=x*z;
		var yz:Number=y*z;
		
		var xs:Number=x*s;
		var ys:Number=y*s;
		var zs:Number=z*s;
		
		m11=xx*(1-c)+c;m12=xy*(1-c)+zs;m13=xz*(1-c)-ys;//m14=0;
		m21=xz*(1-c)-zs;m22=yy*(1-c)+c;m23=yz*(1-c)+xs;//m24=0;
		m31=xz*(1-c)+ys;m32=yz*(1-c)-xs;m33=zz*(1-c)+c;//m34=0;
		//m41=0;m42=0;m43=0;m44=1;
		 
	}
	//缩放
	public function setupScale(xscale:Number,yscale:Number,zscale:Number):void{
		m11=xscale;m12=0;m13=0;m14=0;
		m21=0;m22=yscale;m23=0;m24=0;
		m31=0;m32=0;m33=zscale;m34=0;
		m41=0;m42=0;m43=0;m44=1;
	}
	//不实现
	//public function setupScale(){}
	//向z=D的平面投影，
	/*
	public function setupProject(D:Number){
		m11=1;m12=0;m13=0;m14=0;
		m21=0;m22=1;m23=0;m24=0;
		m31=0;m32=0;m33=1;m34=D;
		m41=0;m42=0;m43=0;m44=0;
	}
	//*/
	//
	public static function SMul_MM(a:Matrix3D,b:Matrix3D,c:Matrix3D):void{
		/*
		c.m11=a.m11*b.m11+a.m12*b.m21+a.m13*b.m31+a.m14*b.m41;
		c.m12=a.m11*b.m12+a.m12*b.m22+a.m13*b.m32+a.m14*b.m42;
		c.m13=a.m11*b.m13+a.m12*b.m23+a.m13*b.m33+a.m14*b.m43;
		c.m14=a.m11*b.m14+a.m12*b.m24+a.m13*b.m34+a.m14*b.m44;
		
		c.m21=a.m21*b.m11+a.m22*b.m21+a.m23*b.m31+a.m24*b.m41;
		c.m22=a.m21*b.m12+a.m22*b.m22+a.m23*b.m32+a.m24*b.m42;
		c.m23=a.m21*b.m13+a.m22*b.m23+a.m23*b.m33+a.m24*b.m43;
		c.m24=a.m21*b.m14+a.m22*b.m24+a.m23*b.m34+a.m24*b.m44;
		
		c.m31=a.m31*b.m11+a.m32*b.m21+a.m33*b.m31+a.m34*b.m41;
		c.m32=a.m31*b.m12+a.m32*b.m22+a.m33*b.m32+a.m34*b.m42;
		c.m33=a.m31*b.m13+a.m32*b.m23+a.m33*b.m33+a.m34*b.m43;
		c.m34=a.m31*b.m14+a.m32*b.m24+a.m33*b.m34+a.m34*b.m44;
		
		c.m41=a.m41*b.m11+a.m42*b.m21+a.m43*b.m31+a.m44*b.m41;
		c.m42=a.m41*b.m12+a.m42*b.m22+a.m43*b.m32+a.m44*b.m42;
		c.m43=a.m41*b.m13+a.m42*b.m23+a.m43*b.m33+a.m44*b.m43;
		c.m44=a.m41*b.m14+a.m42*b.m24+a.m43*b.m34+a.m44*b.m44;
		//*/
		
		////////////////快速4*3
		//*
		c.m11=a.m11*b.m11+a.m12*b.m21+a.m13*b.m31;
		c.m12=a.m11*b.m12+a.m12*b.m22+a.m13*b.m32;
		c.m13=a.m11*b.m13+a.m12*b.m23+a.m13*b.m33;
		c.m14=0;
		
		c.m21=a.m21*b.m11+a.m22*b.m21+a.m23*b.m31;
		c.m22=a.m21*b.m12+a.m22*b.m22+a.m23*b.m32;
		c.m23=a.m21*b.m13+a.m22*b.m23+a.m23*b.m33;
		c.m24=0;
		
		c.m31=a.m31*b.m11+a.m32*b.m21+a.m33*b.m31;
		c.m32=a.m31*b.m12+a.m32*b.m22+a.m33*b.m32;
		c.m33=a.m31*b.m13+a.m32*b.m23+a.m33*b.m33;
		c.m34=0;
		
		c.m41=a.m41*b.m11+a.m42*b.m21+a.m43*b.m31+b.m41;
		c.m42=a.m41*b.m12+a.m42*b.m22+a.m43*b.m32+b.m42;
		c.m43=a.m41*b.m13+a.m42*b.m23+a.m43*b.m33+b.m43;
		c.m44=1;
		//*/
	}
	//设置lookAt矩阵
	
	////////////////////////
	 /**
   * 返回null标志着是不能构造正确的lookAt()矩阵，把解决这种问题的处理应用处理自己解决（以后再想办法解决）。程序使用时需要自己想办法
   * 返回null有两种情况，当n=0或u=0时，即焦点和自己的位置重合、视线方向和头顶方向重合，
   * 注意摄象机是屁股朝向目标的
   * @param c Vector3D 盯住的目标
   * @param vrp Vector3D 眼睛
   * @param up Vector3D “上”的方向
   * @return Transform
   * 
   * return 是很有用的，当失败的时候原地返回，上一个矩阵是有效的，即可以用上一个来显示现在的
   */
  //这个是给摄像机用的
 public  function setupCameraLookAt(c:Vector3D , vrp: Vector3D, up:Vector3D ):void {
 	
 	//这3个以前是做全局变量保存的,这里做局部变量,节约资源据说
 		var u:Vector3D = new Vector3D(0,0,0);//x轴
	 	var v:Vector3D = new Vector3D(0,0,0);//y轴
	  	var n:Vector3D = new Vector3D(0,0,0);//z轴
 	
 	
 	//this.Identity();
 	//up = new Vector3D(0,1,0);//上
  
    //获得n
    Vector3D.SSub(vrp,c,n);

    if (n.isZero()) { //n是可能为0的，当位置和焦点重合时
     T.p("n为0");
      return ;
    }

    n.normalize();

    //获得u
    Vector3D.SOuterProduct(up,n,u);

    if (u.isZero()) {
     T.p("up和n平行了");
      return ;
    }

    u.normalize();

    //获得v
    Vector3D.SOuterProduct(n,u,v);
    v.normalize();

    ////////组装矩阵
   
    /*
     ux  uy  uz  0//转换矩阵的 每个行向量都可以看做是新坐标系的x轴在原坐标系中的坐标，
     vx  vy  vz  0
     nx  ny  nz  0
     dx  dy  dz  1
     //*/
     /*
	m11=u.x;m12=u.y;m13=u.z;m14=0;
	m21=v.x;m22=v.y;m23=v.z;m24=0;
	m31=n.x;m32=n.y;m33=n.z;m34=0;
	m41=c.x;m42=c.y;m43=c.z;m44=1;
	//*/
	//trace("输入");
	//this.trace();
	/*
	m11=1;m12=0;m13=0;m14=0;
	m21=0;m22=1;m23=0;m24=0;
	m31=0;m32=0;m33=1;m34=0;
	m41=c.x;m42=c.y;m43=c.z;m44=1;
	//*/
	var uc:Number=Vector3D.SInnerProduct(u,c);
	var vc:Number=Vector3D.SInnerProduct(v,c);
	var nc:Number=Vector3D.SInnerProduct(n,c);
	  //*
	m11=u.x;m12=v.x;m13=n.x;//m14=0;
	m21=u.y;m22=v.y;m23=n.y;//m24=0;
	m31=u.z;m32=v.z;m33=n.z;//m34=0;
	m41=-uc;m42=-vc;m43=-nc;//m44=1;
	//*/
  }
  
  //让物体朝某一方向用的
  //矩阵的每一行可以看成是变换后的基向量
  public  function setupLookAt(c:Vector3D , vrp: Vector3D, up:Vector3D ):void {
 	
 	//这3个以前是做全局变量保存的,这里做局部变量,节约资源据说
 		var u:Vector3D = new Vector3D(0,0,0);//x轴
	 	var v:Vector3D = new Vector3D(0,0,0);//y轴
	  	var n:Vector3D = new Vector3D(0,0,0);//z轴
 	
 	
 	//this.Identity();
 	//up = new Vector3D(0,1,0);//上
  
    //获得n(z基向量)
    Vector3D.SSub(vrp,c,n);

    if (n.isZero()) { //n是可能为0的，当位置和焦点重合时
     T.p("n为0");
      return ;
    }

    n.normalize();

    //获得u(x基向量)
    Vector3D.SOuterProduct(up,n,u);

    if (u.isZero()) {
     T.p("up和n平行了");
      return ;
    }

    u.normalize();

    //获得v(y基向量)
    Vector3D.SOuterProduct(n,u,v);
    v.normalize();

    ////////组装矩阵
   
    /*
     ux  uy  uz  0//转换矩阵的 每个行向量都可以看做是新坐标系的x轴在原坐标系中的坐标，
     vx  vy  vz  0
     nx  ny  nz  0
     dx  dy  dz  1
     //*/
    
	m11=u.x;m12=u.y;m13=u.z;//m14=0;
	m21=v.x;m22=v.y;m23=v.z;//m24=0;
	m31=n.x;m32=n.y;m33=n.z;//m34=0;
	m41=c.x;m42=c.y;m43=c.z;//m44=1;
  }
	////////////////////////
	//Vector3D*Matrix3D,c是结果，
	public static function SMul_VM(v:Vector3D,m:Matrix3D,c:Vector3D):void{
		c.x=v.x*m.m11+v.y*m.m21+v.z*m.m31+m.m41;
		c.y=v.x*m.m12+v.y*m.m22+v.z*m.m32+m.m42;
		c.z=v.x*m.m13+v.y*m.m23+v.z*m.m33+m.m43;
		//c.trace();
	}
	
//	//Vertex3D*Matrix3D,这个主要用法是把物体坐标系的顶点投影到摄像机空间
//	public static function SMul_Vertex_M(v:Vertex3D,m:Matrix3D,c:Vector3D):void{
//		c.x=v.x*m.m11+v.y*m.m21+v.z*m.m31+m.m41;
//		c.y=v.x*m.m12+v.y*m.m22+v.z*m.m32+m.m42;
//		c.z=v.x*m.m13+v.y*m.m23+v.z*m.m33+m.m43;
//		//c.trace();
//	}
	
	//Vertex3D*Matrix3D,这个主要用法是把物体坐标系的顶点投影到摄像机空间
	public static function SMul_Vertex_M(v:Vertex3D,m:Matrix3D):void{
		v.screenX=v.x*m.m11+v.y*m.m21+v.z*m.m31+m.m41;
		v.screenY=v.x*m.m12+v.y*m.m22+v.z*m.m32+m.m42;
		v.screenZ=v.x*m.m13+v.y*m.m23+v.z*m.m33+m.m43;
		//c.trace();
	}
	
	/*
	//V,M,返回的是一个Array
	public static function SMul_VM_2(v:Vector3D,m:Matrix3D,a:Array,startIdx:Number){
		a[startIdx]=v.x*m.m11+v.y*m.m21+v.z*m.m31+m.m41;//x
		a[startIdx+1]=v.x*m.m12+v.y*m.m22+v.z*m.m32+m.m42;//y
		a[startIdx+2]=v.x*m.m13+v.y*m.m23+v.z*m.m33+m.m43;//z
		//c.trace();
	}
	//*/
	
	//把数组中特定的3个数转化成另一个数组中的特定的3个
	public static function SMul_VM_3(a:Vector.<Number>,startIdx0:int,m:Matrix3D,b:Vector.<Number>,startIdx:int):void{
		var x:Number=a[startIdx0];
		var y:Number=a[startIdx0+1];
		var z:Number=a[startIdx0+2];
		
		b[startIdx]=x*m.m11+y*m.m21+z*m.m31+m.m41;//x
		b[startIdx+1]=x*m.m12+y*m.m22+z*m.m32+m.m42;//y
		b[startIdx+2]=x*m.m13+y*m.m23+z*m.m33+m.m43;//z
		//c.trace();
	}
	
	//求矩阵的逆矩阵相关
	//计算矩阵的行列式
	public function determinant():Number{
		return m11*(m22*m33-m23*m32)
			+m12*(m23*m31-m21*m33)
			+m13*(m21*m32-m22*m31);
	}
	
	//求某矩阵的逆，保存在aim中
	public static function SInverse(m:Matrix3D,r:Matrix3D):void{
		var det:Number=m.determinant();
		var oneOverDet:Number=1/det;
		
		r.m11=(m.m22*m.m33-m.m23*m.m32)*oneOverDet;
		r.m12=(m.m13*m.m32-m.m12*m.m33)*oneOverDet;
		r.m13=(m.m12*m.m23-m.m13*m.m22)*oneOverDet;
		
		r.m21=(m.m23*m.m31-m.m21*m.m33)*oneOverDet;
		r.m22=(m.m11*m.m33-m.m13*m.m31)*oneOverDet;
		r.m23=(m.m13*m.m21-m.m11*m.m23)*oneOverDet;
		
		r.m31=(m.m21*m.m32-m.m22*m.m31)*oneOverDet;
		r.m32=(m.m12*m.m31-m.m11*m.m32)*oneOverDet;
		r.m33=(m.m11*m.m22-m.m12*m.m21)*oneOverDet;
		
		r.m41=-(m.m41*r.m11+m.m42*r.m21+m.m43*r.m31);
		r.m42=-(m.m41*r.m12+m.m42*r.m22+m.m43*r.m32);
		r.m43=-(m.m41*r.m13+m.m42*r.m23+m.m43*r.m33);
	}
}//end of class
}